今天要討論的是Enum,Enum是個很有趣的東西,它是Object的一種變形,而Enum最重要的優點就是能提升程式碼的可讀性,所以接下來要好好來認識Enum,以後充分運用。
Enum是一群有關聯性、以可讀性比較高的一群常數值集合,這邊的常數值可以是number或string型別。
以下先以常數值為number型別的範例來認識Enum:
enum Progress {
NotStarted,
InProgress,
IsCompleted
}
假設現在希望用一種方式來表現工作的進度,進度分為三種:尚未開始(not started)、進行中(in process)、已完成(is completed),這裡我們先用enum型別來討論如何描述現在這樣的狀況。
Progress代表一項工作的進度,這裡一樣用NotStarted、InProgress、IsCompleted表示三種進度。而Enum型別預設會以NotStarted當作number型別常數值0的鍵值(key),接著InProgress和IsComplete自動依序代表number型別常數值1和2的鍵值,也就是說上面的例子就等同於:
enum Progress {
NotStarted = 0,
InProgress = 1,
IsCompleted = 2
}
這是在沒有任何指定下的enum預設值。
當然也可以初始化第一個鍵值代表的常數值,接下來的常數就會依序自動給值,例如在用Enum表達月份的時候,可能會希望常數值和鍵值相呼應:
enum Mounth {
January = 1,
February, // 2
March, // 3
April, // 4
May, // 5
June, // 6
July, // 7
August, // 8
September, // 9
October, // 10
November, // 11
December // 12
或者也可以自行給定每個鍵值所代表的初始值:
enum Quarter {
Q1 = 0,
Q2 = 25,
Q3 = 50,
Q4 = 75
}
前面有提到常數值也可以是string型別,例如:
enum Mounth {
January = "Jan",
February = "Feb",
March = "Mar",
April = "Apr",
May = "May",
June = "Jun",
July = "Jul",
August = "Aug",
September = "Sep",
October = "Oct",
November = "Nove",
December = "Dec"
Enum主要應用的情境如下:
回到Progess範例用一個極簡的例子來理解,假設這裡不使用Enum而是先單純以0、1、2來代表not started、not started、in progress、is completed、is completed三種開發任務的進度:
/*
Progress
0: not started
1: in progress
2: is completed
*/
let develop_tasks = {
feature1: 2,
feature2: 1,
feature3: 0,
feature4: 2,
}
function checkTaskProgress(feature){
let progrss = ;
switch(develop_task['feature']){
case 0:
progress = 'not started';
break;
case 1:
progress = 'in progress';
break;
case 2:
progress = 'is completed';
break;
default:
return new Error('undefined value');
};
return progress
}
這一小段不太好的程式碼雖然在自己練習或是小型專案看似可行,但是當程式碼愈來愈多或專案愈來愈龐大,如果增加數值來表示其他種「進度」,開發到後期可能就會忘記這些數值的進度意義,而必須耗費時間去看註解或是文件等。
另外,未來若要修改數值所表示的進度,可能就變得比較難維護,因為除了要更動 checkTaskProgress
裡 switch
回傳的進度,也需要修改原來develop_tasks
每個 feature*
的進度數值,可能同時影響到其他有使用到這些進度數值的「工作」。
那如果精進一下程式碼,以一個物件 progress
表示進度和其數值:
const progress = {
notStarted: 0,
inProgress: 1,
isCompleted: 2,
};
let develop_tasks = {
feature1: progress.isCompleted,
feature2: progress.inProgress,
feature3: progress.notStarted,
feature4: progress.isCompleted,
}
function checkTaskProgress(feature){
switch(develop_task['feature']){
case progress['notStarted']:
return 'not started';
case progress['inProgress']:
return 'in progress';
case progress['isCompleted']:
return 'is completed';
};
return new Error('undefined value');
}
這邊使用object型別已經改善最前面提到的幾個問題,但是要注意的是,object的 const
關鍵字不是指鍵值(key)和值(value)之間是常數關係,而是指 progress
這個object名字所參考到的物件是不可變動的,也因此稍有不慎, progress
鍵值的數值是可以被更改的。
這裡新增一個函式來確認一個任務是否尚未開始開發(not started),同時模擬不小心將 progress
object的 notStarted
屬性值改成 -1
(ˋ注意:這是可行的):
function isNotStarted(progress){
if(!progress) return true;
return false;
}
// ...
progress.notStarted = -1; // ok
console.log(isNotStarted(feature3)); // false
因為 number 型別只有只有 0 才是falsy value,所以改成-1
之後理所當然會回傳 false
,而這樣的小錯誤可能就會導致程式碼後續會出現更多問題,所以 Enum 型別的常數值就能避免這樣的事情發生。
考慮到篇幅,以及為了加緊整個系列的腳步,今天只從一個菜鳥的角度簡單認識Enum的優點和應用,若想繼續深入Enum,可以官方文件搜尋Enum,或是參考TypeScript HandBook 的 Enum 章節。
參考資料
TypeScript: JavaScript With Syntax For Types
TypeScript Tutorial
W3Schools Online Web Tutorials